import * as faceLandmarksDetection from "@tensorflow-models/face-landmarks-detection";
import * as tf from "@tensorflow/tfjs-core";
import "@tensorflow/tfjs-backend-webgl";

let model, video, event, blinkRate;
let blinkCount = 0;
let tempBlinkRate = 0;
let rendering = false;
let rateInterval;
const EAR_THRESHOLD = 0.27;

function initBlinkRateCalculator() {
  console.log("Initializing blink rate calculator...");
  rateInterval = setInterval(() => {
    blinkRate = tempBlinkRate * 6;
    tempBlinkRate = 0;
    console.log("Blink rate updated:", blinkRate, "blinks/minute");
  }, 10000);
}

const loadModel = async () => {
  console.log("Loading blink detection model...");
  await tf.setBackend("webgl");

  // Try using the mediapipe runtime for more reliable face detection.
  // Also, specifying solutionPath for the mediapipe model.
  model = await faceLandmarksDetection.createDetector(
    faceLandmarksDetection.SupportedModels.MediaPipeFaceMesh,
    {
      runtime: "mediapipe",
      refineLandmarks: true,
      maxFaces: 1,
      solutionPath: "https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh/",
    }
  );

  console.log("Blink detection model loaded:", model);
};

const initBlink = (videoElement) => {
  console.log("Initializing blink detection with given video element...");
  video = videoElement;
  if (video) {
    console.log("Video element found:", video);
    console.log("Video readyState:", video.readyState);
    console.log("Video dimensions:", video.videoWidth, "x", video.videoHeight);
  } else {
    console.warn("No video element provided to initBlink!");
  }
  rendering = true; // Enable rendering now that we have a video
  initBlinkRateCalculator();
};

function stopPrediction() {
  console.log("Stopping blink predictions...");
  rendering = false;
  clearInterval(rateInterval);
}

function updateBlinkRate() {
  tempBlinkRate++;
  console.log("Blink detected, incrementing temp blink rate:", tempBlinkRate);
}

function getEucledianDistance(x1, y1, x2, y2) {
  return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
}

function getEAR(upper, lower) {
  const earVal =
    (getEucledianDistance(upper[5][0], upper[5][1], lower[4][0], lower[4][1]) +
      getEucledianDistance(
        upper[3][0],
        upper[3][1],
        lower[2][0],
        lower[2][1]
      )) /
    (2 *
      getEucledianDistance(upper[0][0], upper[0][1], upper[8][0], upper[8][1]));

  return earVal;
}

function isVoluntaryBlink(blinkDetected) {
  if (blinkDetected) {
    blinkCount++;
    if (blinkCount > 4) {
      blinkCount = 0;
      return true;
    }
  } else {
    blinkCount = 0;
  }
  return false;
}

async function renderPrediction() {
  if (!rendering) {
    console.log("renderPrediction() called but rendering is false. Returning.");
    return event;
  }
  if (!model) {
    console.log("renderPrediction() called but model is not loaded.");
    return event;
  }
  if (!video) {
    console.log("renderPrediction() called but video is not set.");
    return event;
  }

  if (video.readyState !== 4) {
    console.log("Video not ready, readyState:", video.readyState);
    return event;
  }

  console.log("Running face detection...");
  // Try flipping horizontally if user-facing camera is used
  const predictions = await model.estimateFaces(video, {
    flipHorizontal: true,
  });
  console.log("Predictions obtained:", predictions);

  if (predictions.length > 0) {
    predictions.forEach((prediction, i) => {
      console.log(`Processing prediction #${i}:`, prediction);
      let lowerRight = prediction.annotations.rightEyeUpper0;
      let upperRight = prediction.annotations.rightEyeLower0;
      const rightEAR = getEAR(upperRight, lowerRight);
      console.log("Right EAR:", rightEAR);

      let lowerLeft = prediction.annotations.leftEyeUpper0;
      let upperLeft = prediction.annotations.leftEyeLower0;
      const leftEAR = getEAR(upperLeft, lowerLeft);
      console.log("Left EAR:", leftEAR);

      let blinked = leftEAR <= EAR_THRESHOLD && rightEAR <= EAR_THRESHOLD;
      console.log("Blinked?", blinked);

      if (blinked) {
        updateBlinkRate();
      }

      event = {
        left: leftEAR <= EAR_THRESHOLD,
        right: rightEAR <= EAR_THRESHOLD,
        wink: leftEAR <= EAR_THRESHOLD || rightEAR <= EAR_THRESHOLD,
        blink: blinked,
        longBlink: isVoluntaryBlink(blinked),
        rate: blinkRate,
      };
      console.log("Event:", event);
    });
  } else {
    console.log(
      "No faces detected. Ensure you're facing the camera with good lighting."
    );
    event = {
      blink: false,
      wink: false,
      left: false,
      right: false,
      longBlink: false,
      rate: blinkRate,
    };
  }

  return event;
}

const blink = {
  loadModel,
  initBlink,
  stopPrediction,
  getBlinkPrediction: renderPrediction,
};

export default blink;
